import exception.AuthenticationLoginFailure;
import exception.AuthenticationPasswordFailure;
import exception.CustomerNotRegistered;
import exception.EmployeeAlreadyInDepartment;
import exception.EmployeeLoginExists;
import exception.InsuranceExists;
import exception.NoDamageHistory;
import exception.NoInsuranceForThisCar;
import exception.NoSuchClaim;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.Locale;
import java.util.logging.Level;
import java.util.logging.Logger;

import metier.FinantialTransactionStub;
import metier.ResponseForm;
import metier.ResponseLetter;
import modele.Claim;
import modele.Company;
import modele.ComplexClaim;
import modele.Customer;
import modele.Department;
import modele.Employee;
import modele.Expense;
import modele.Garage;
import modele.Insurance;
import modele.Paiement;
import modele.SimpleClaim;
import util.Saisie;

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/**
 *
 * @author Alex
 */
public class Main {
    private Company comp;
    private Employee emp;
    private static final String mainMenu =
            "What do you want to do now ?\n\n" +
            "\t0 : Quit\n" +
            "\t1 : Authenticate\n" +
            "\nYour choice : ";
            
    private final String menuForCDEmployee = 
            "What do you want to do now ?\n\n" +
            "\t0 : Quit\n" +
            "\t1 : Register claim\n" +
            "\t2 : Classify claim\n" + 
            "\t3 : Generate Form\n" +
            "\t4 : Set form answers/validate form\n" +
            "\t5 : Process claim\n" +
            "\t6 : Take a decision\n" +
            "\t7 : Consult garage information\n" +
            "\t8 : Print all claims' status\n" +
            "\nYour choice : ";
    private final String menuForFinanceEmployee = 
            "What do you want to do now ?\n\n" +
            "\t0 : Quit\n" +
            "\t1 : Process paiement\n" +
            "\t2 : Consult garage information\n" +
            "\t3 : Print all claims' status\n" +
            "\nYour choice : ";
    private final String menuForAdministrator =
            "What do you want to do now ?\n\n" +
            "\t0 : Quit\n" +
            "\t1 : Create employee\n" +
            "\t2 : Consult garage information\n" +
            "\t3 : Print all claims' status\n" +
            "\nYour choice : ";
    
    public static void main(String[] args) {
        Main program = new Main();
        program.initContext();
        boolean stop = false;
        while(!stop) {
            Integer choice = new Integer(-1);
            try {
                choice = Integer.parseInt(Saisie.lireChaine(mainMenu));
            } catch (Exception e) {
                System.out.println("Not a valid number. Program termination.");
                stop = true;
                break;
            }
            
            switch(choice) {
                case 0:
                    stop = true;
                    break;
                case 1:
                    boolean authenticated = false;
                    while (!authenticated) {
                        authenticated = program.authentication();
                    }
                    program.employeeEveryDayWork();
                default:
                    break;
            }
        }
    }
    
    private String getMenuForCDEmployee(Employee emp) {
        String result = menuForCDEmployee;
        if (emp.getRank() == Employee.Rank.LOW) {
            result = result.replaceAll("\t6 : Take a decision\n", "");
        }
        return result;
    }
    
    private void initContext() {
        Company cmp = new Company("Insurance");
        /* Departments creation */
        Department dpt1 = new Department("CD");
        Department dpt2 = new Department("Finance");
        Department dpt3 = new Department("Admin");
        
        /* Garages creation */
        Garage gar1 = new Garage("GAR01", "Renault", "2 av. xxx", "010101",
                "1111 111 11");
        Garage gar2 = new Garage("GAR02", "Peugeot", "4 rd. yyy", "020202",
                "2222 222 22");
        
        /* Employees creation */
        Employee emp1 = new Employee("Alexis", "Legay", "alegay", "pass1",
                Employee.Rank.HIGH);
        Employee emp2 = new Employee("Luc", "Mazon", "lmazon", "pass2",
                Employee.Rank.HIGH);
        Employee emp3 = new Employee("Ad", "ministrator", "admin", "admin",
                Employee.Rank.HIGH);
        Employee emp4 = new Employee("Toc", "ard", "tocard", "tocard",
                Employee.Rank.LOW);
        
        /* Customers creation */
        Customer cus1 = new Customer("Client", "Robert Dupont");
        cus1.setIdCustomer(1);
        try {
            Insurance ins1 = new Insurance("XXX", Insurance.Type.ALLRISK);
            ins1.setId(1);
            cus1.addInsurance(ins1);
        } catch (InsuranceExists ex) {
            System.out.println(ex);
        }
        Customer cus2 = new Customer("Client", "Jean Delavigne");
        cus2.setIdCustomer(2);
        try {
            Insurance ins2 = new Insurance("YYY", Insurance.Type.ALLRISK);
            ins2.setId(2);
            cus2.addInsurance(ins2);
        } catch (InsuranceExists ex) {
            System.out.println(ex);
        }
        
        /* Objects relations creations */
        try {
            dpt1.addEmployee(emp1);
            emp1.setDepartment(dpt1);
            dpt1.addEmployee(emp4);
            emp4.setDepartment(dpt1);
            dpt2.addEmployee(emp2);
            emp2.setDepartment(dpt2);
            dpt3.addEmployee(emp3);
            emp3.setDepartment(dpt3);
        } catch (EmployeeLoginExists | EmployeeAlreadyInDepartment ex) {
            Logger.getLogger(Main.class.getName()).log(Level.SEVERE,
                    null, ex);
        }
        cmp.addDepartment(dpt1);
        cmp.addDepartment(dpt2);
        cmp.addDepartment(dpt3);
        
        cmp.addCustomer(cus1);
        cmp.addCustomer(cus2);
        
        cmp.addGarage(gar1);
        cmp.addGarage(gar2);
        
        this.comp = cmp;
    }
    
    private boolean authentication() {
        boolean result = false;
        System.out.println("########### Authentication ###########\n");
        String login = Saisie.lireChaine("Login : ");
        String password = Saisie.lireChaine("Password : ");
        try {
            emp = comp.authenticateUser(login, password);
            System.out.println("\nWelcome " + emp.getFirstName() + " !");
            result = true;
        } catch (AuthenticationPasswordFailure | AuthenticationLoginFailure ex) {
            /*Logger.getLogger(Main.class.getName()).log(Level.SEVERE,
                    null, ex);*/
            System.out.println("\nWrong combination Login/password. Try again !");
        }
        finally {
            System.out.println("\n###################################\n");
            return result;
        }
    }
    
    private void employeeEveryDayWork() {
        Integer choice = new Integer(-1);
        switch (emp.getDepartment().getName()) {
            case "CD":
                while (!choice.equals(0)) {
                    try {
                        choice = Integer.parseInt(
                                Saisie.lireChaine(getMenuForCDEmployee(emp)));
                        switch (choice) {
                            case 0:
                                break;
                            case 1:
                                /* Register claim */
                                registerClaim();
                                break;
                            case 2:
                                /* Classify claim */
                                classifyClaim();
                                break;
                            case 3:
                                /* generate the form to be filled by the customer */
                                generateForm();
                                break;
                            case 4:
                                /* set the answer fields of the form */
                                setFields();
                                break;
                            case 5:
                                /* process the claim depending upon it's difficulty */
                                processClaim();
                                break;
                            case 6:
                                /* take a decision about a claim, then generate 
                                 the associated response letter */
                                if (emp.getRank() == Employee.Rank.HIGH) {
                                    takeDecision();
                                }
                                break;
                            case 7:
                                /* Consult garage information */
                                consultGarageInfo();
                                break;
                            case 8:
                                /* Claims' status */
                                printAllClaims();
                                break;
                            default:
                                break;
                        }
                    }
                    catch ( NumberFormatException e) {
                        System.out.println("Enter a number please.");
                    }
                }
                break;
            case "Finance":
                while (!choice.equals(0)) {
                    try {
                    choice = Integer.parseInt(
                            Saisie.lireChaine(menuForFinanceEmployee));
                    switch (choice) {
                        case 0:
                            break;
                        case 1:
                            /* Process paiement */
                            processPaiement();
                            break;
                        case 2:
                            /* Consult garage information */
                            consultGarageInfo();
                            break;
                        case 3:
                            /* Claims' status */
                            printAllClaims();
                            break;
                        default:
                            System.out.println("Unknown choice...");
                            break;
                    }
                    }catch (NumberFormatException e) {
                        System.out.println("Enter a number please.");
                    }
                }
                break;
            case "Admin":
                while (!choice.equals(0)) {
                    try {
                    choice = Integer.parseInt(
                            Saisie.lireChaine(menuForAdministrator));
                    switch (choice) {
                        case 0:
                            break;
                        case 1:
                            /* Create employee */
                            createEmployee();
                            break;
                        case 2:
                            /* Consult garage information */
                            consultGarageInfo();
                            break;
                        case 3:
                            /* Claims' status */
                            printAllClaims();
                            break;
                        default:
                            System.out.println("Unknown choice...");
                            break;
                    }
                    }catch (NumberFormatException e) {
                        System.out.println("Enter a number please.");
                    }
                }
                break;
            default:
                System.out.println("How lucky you are, no work to do !");
        }
    }
    
    private void registerClaim() {
        System.out.println("\n########### Claim registration ###########\n");
        
        /* Claim creation */
            Claim claim = new Claim();
            
        /* Is the sender of the claim a customer of the company ? */
        try {
            Integer Id = Integer.parseInt(Saisie.lireChaine("Customer ID : "));
            Customer customer = comp.getCustomer(Id);
            /* Customer OK */
            claim.setCustomer(customer);

            /* Fill information about the claim */
           
            /* Responsability */
            System.out.println("Press 1 for a responsible accident, anything " +
                    "otherwise.");
            String choiceResponsability = Saisie.lireChaine("Your choice : ");
            if (choiceResponsability.equals("1")) {
                claim.setResponsibility(Claim.Responsibility.RESPONSIBLE);
            }
            
            /* Expenses */
            System.out.println("Press 1 to add an expense to the claim or "
                    + "whatever number to skip this step.");
            Integer choice = Integer.parseInt(Saisie.lireChaine("Your choice"
                    + " : "));
            while ( choice.equals(1)) {
                String desc = Saisie.lireChaine("Short description about "
                        + "the nature of the expense : ");
                Float amount = Float.parseFloat(Saisie.lireChaine("Amount "
                        + "paid : "));
                if ( amount < 0 ) {
                    System.out.println("Negative amount given, registration "
                            + "aborted...");
                    return;
                }
                Expense exp = new Expense(desc, amount);
                claim.addExpense(exp);
                System.out.println("The expense has been added to the claim.");
                
                System.out.println("Press 1 to add another expense to the "
                        + "claim or whatever number to continue the "
                        + "registration process.");
                choice = Integer.parseInt(Saisie.lireChaine("Your choice"
                        + " : "));
            }
            
            /* Accident date */
            SimpleDateFormat formatter = new SimpleDateFormat("dd/MM/yy");
            GregorianCalendar gc = new GregorianCalendar(Locale.FRANCE);
            
            Boolean validDate = false;
            Integer tries = 0;
            while (!validDate && tries < 3) {
                try {
                    gc.setTime(formatter.parse(Saisie.lireChaine("Accident "
                            + "date (dd/mm/yy) : ")));
                    if (gc.getTime().before(new GregorianCalendar().getTime())) {
                        System.out.println("Date understood : " +
                                gc.getTime().toString());
                        validDate = true;
                    } else if (gc.getTime().after(new GregorianCalendar().getTime())){
                        System.out.println("Date in the future. Try again.");
                    }
                    else {
                        System.out.println("Unknown date. The format should be "
                           + ": dd/mm/yy");
                    }
                } catch (ParseException ex) {
                    System.out.println("Unknown date. The format should be "
                            + ": dd/mm/yy");
                }
                tries++;
            }
            if (!validDate) {
                System.out.println("3 errors in date capture, "
                        + "registration aborted...");
                return;
            }
            
            claim.setAccidentDate(gc);
            
            /* Save the claim in the system */
            /* Temporary solution while no DB */
            claim.setIdClaim(Claim.getNumberOfClaim());
            
            comp.getCustomer(Id).addClaim(claim);
            comp.registerClaim(claim);
            
            System.out.println("The claim is now registered.");
        } 
        catch (CustomerNotRegistered ex1) {
            claim = null;
            System.out.println("Unknown ID, try with its name");
            /* Implement research with name and add the choice
             * at the beginning of the method */
        }
        catch (NumberFormatException ex2) {
            claim = null;
            System.out.println("Incorrect type given while a number was "
                    + "required, registration aborted...");
        }
        finally {
            System.out.println("\n###################################\n");
        }
    }
    
    private void classifyClaim() {
        System.out.println("\n########### Claim classification ###########\n");
        if (comp.getNumberRegisteredClaims().equals(0)) {
            System.out.println("No registered claim at this time !");
        }
        else {
            System.out.println("Which one of the following claims do you"
                    + " want to classify ?\n");
            Collection<Claim> claimsList = comp.getRegisteredClaims();
            
            for (Claim c : claimsList) {
                System.out.println("\t" + c.getIdClaim() + ". Claim from '" +
                        c.getCustomer().toString() + "'");
            }

            try {
                Claim selectedClaim = null;
                Integer tries = 0;
                System.out.println("\nPlease select the claim by providing its number "
                + "as given in the previous list.");
                while ( (selectedClaim == null ) && ( tries < 3) ) {
                    Integer choice = Integer.parseInt(Saisie.lireChaine("Your choice : "));
                    for (Claim c : claimsList) {
                        if (c.getIdClaim() == choice) {
                            selectedClaim = c;
                        }
                    }
                    tries++;
                }
                if (selectedClaim == null) {
                    System.out.println("3 errors in the claim choice,"
                            + "classification aborted...");
                    return;
                }
                
                System.out.println("Available types of claims :");
                Claim classifiedClaim;
                Integer claimType = Integer.parseInt(
                        Saisie.lireChaine("\n\t1.Simple claim\n\t2.Complex claim"
                        + "\n\nYour choice : "));
                switch (claimType) {
                    case 1:
                        classifiedClaim = new SimpleClaim(selectedClaim);
                        break;
                    case 2:
                        classifiedClaim = new ComplexClaim(selectedClaim);
                        break;
                    default:
                        System.out.println("Unknown choice, classification "
                            + "aborted...");
                        return;
                }
                
                /* Replace old claim by new one (classified) */
                comp.replaceClaimForClassification(selectedClaim, classifiedClaim);
                System.out.println("The claim is now classified.");
                
            }
            catch ( NumberFormatException e ) {
                System.out.println("Incorrect type given while a number was "
                    + "required, classification  aborted...");
            }
        }
        System.out.println("\n###################################\n");
    }
    
    public void generateResponseLetter() {
        Customer customer = null;
        Integer tries = 0;
        while (customer == null && tries < 3) {
            try {
                Integer customerId = Integer.parseInt(Saisie.lireChaine(
                        "\nCustomer ID : ") );
                for (Customer c : comp.getCustomers()) {
                    if (c.getIdCustomer() == customerId) {
                        customer = c;
                    }
                }
            } catch (NumberFormatException e) {
                System.out.println("A number is required.");
            }
            if (customer == null) {
                System.out.println("Unknown customer.");
            }
            tries++;
        }
        if (customer == null ) {
            System.out.println("3 errors in the customer choice, response "
                    + "letter generation aborted...");
            return;
        }
/*        try {
            System.out.println("\nResponse letter generated :");
            System.out.println("####################");
            System.out.println(new ResponseLetter(customer, emp).getResult());
            System.out.println("####################\n");
        } catch (NoDamageHistory ex) {
            System.out.println("\nNo damage history.");
        }*/
    }

    private void generateForm() {
        Claim claim = null;
        try {
            claim = this.askForClassifiedClaim(
                "\n######## Form generation ##########\n");
        } catch (NoSuchClaim e) {
            return;
        }
        /* if no ResponseForm was previously created */
        if (!claim.isResponseFormSet()) {
            claim.setResponseForm(new ResponseForm(claim instanceof SimpleClaim ?
                ResponseForm.ClaimType.SIMPLE : ResponseForm.ClaimType.COMPLEX));
        }
        StringBuilder str = new StringBuilder();
        for (String key : claim.getResponseForm().getFields().keySet()) {
            if (!"".equals(claim.getResponseForm().getFields().get(key))) {
                str.append(key).append(" : ").append(claim.getResponseForm().
                        getFields().get(key)).append("\n");
            }
        }
        if ("".equals(str.toString())) {
                System.out.println("No fields currently set.");
        } else {
            System.out.println("The following fields are currently set :\n");
            System.out.println(str.toString());
        }

        System.out.println("\nWhich type of Form would you like to create "+
                "for the remaining fields ?\n" +
                "\t1. Letter\n" +
                "\t2. Email (point to a webform)\n" +
                "\t3. Web form\n");
        Integer tries = 0;
        Integer choice = 0;
        Boolean correct = false;
        ResponseForm.GeneratedType gType = ResponseForm.GeneratedType.LETTER;
        while (!correct && tries < 3) {
            try {
                choice = Integer.parseInt(Saisie.lireChaine("Your choice : "));
            } catch (NumberFormatException e) {
                System.out.println("Incorrect type given while a number was "
                    + "required, form generation aborted...");
                return;
            }
            switch(choice) {
                case 1:
                    gType = ResponseForm.GeneratedType.LETTER;
                    correct=true;
                    break;
                case 2:
                    gType = ResponseForm.GeneratedType.EMAIL;
                    correct=true;
                    break;
                case 3:
                    gType = ResponseForm.GeneratedType.WEBFORM;
                    correct=true;
                    break;
                default:
                    break;
            }
            tries++;
        }
        if (!correct) {
            System.out.println("3 errors in a row. Form generation aborted...");
            System.out.println("\n###################################\n");
            return;
        }
        System.out.println("Fields generated : \n");
        for (String key : claim.getResponseForm().getFields().keySet()) {
            System.out.println(key);
        }

        System.out.println("\nForm sent to the customer :\n###################");
        System.out.println(claim.getResponseForm().generateFormText(claim.getCustomer().
                getFirstName()+" "+claim.getCustomer().getLastName(),
                emp.getFirstName()+" "+emp.getLastName(), gType));         
        System.out.println("###################");
        
        System.out.println("\n###################################\n");
    }

    private void createEmployee() {
        System.out.println("\n########## Employee Creation ##########\n");
        String firstName = Saisie.lireChaine("Employee first name : ");
        String lastName = Saisie.lireChaine("Employee last name : ");
        String password = Saisie.lireChaine("Employee password: ");
        System.out.println("Available Departments :\n");
        Integer i=0;
        ArrayList<Department> dpts = new ArrayList (comp.getDepartments());
        for (Department dpt : dpts) {
            System.out.println("\t"+i+". "+dpt.getName());
            i++;
        }
        boolean valid = false;
        Integer tries = 0;
        Department dpt = null;
        while (!valid && tries < 3) {
            try {
                Integer choice = Integer.parseInt(Saisie.lireChaine("\nYour choice : ")) ;
                if ( ( choice > 0 ) && ( choice < dpts.size() ) ) { // valid choice
                    dpt = dpts.get(choice);
                    valid = true;
                }
                else {
                    System.out.println("Unknown department.");
                }
            } catch (NumberFormatException e) {
                System.out.println("A number is required.");
            }
            tries++;
        }
        if (!valid) {
            System.out.println("Too many errors in department selection, "
                    + "employee creation aborted...");
            System.out.println("\n###################################\n");
            return;
        }
        
        /* Choosing rank of the employee */
        System.out.println("Available ranks :\n");
        System.out.println("\t1. " + Employee.Rank.LOW);
        System.out.println("\t2. " + Employee.Rank.HIGH);
        valid = false;
        tries = 0;
        Employee.Rank rank = null;
        while (!valid && tries < 3) {
            try {
                Integer choice = Integer.parseInt(Saisie.lireChaine("\nYour choice : ")) ;
                if ( choice.equals(1) ) {
                    rank = Employee.Rank.LOW;
                    valid = true;
                }
                else if ( choice.equals(2) ) {
                    rank = Employee.Rank.HIGH;
                    valid = true;
                }
                else {
                    System.out.println("Unknown rank.");
                }
            } catch (NumberFormatException e) {
                System.out.println("A number is required.");
            }
            tries++;
        }
        if (!valid) {
            System.out.println("Too many errors in rank selection, "
                    + "employee creation aborted...");
            System.out.println("\n###################################\n");
            return;
        }
        try {
            String login = firstName.substring(0, 1).toLowerCase()+lastName.toLowerCase();
            System.out.println("Here is your login name : "+login);
            Employee e = new Employee(firstName, lastName, login, password,
                    rank);
            e.setDepartment(dpt);
            dpt.addEmployee(e);
        } catch (EmployeeLoginExists ex) {
            System.out.println("The login already exists");
            return;
        } catch (EmployeeAlreadyInDepartment ex) {
            System.out.println("Employee already in the department.");
            return;
        }
    }

    private void setFields() {
        Claim claim = null;
        try {
            claim = this.askForClassifiedClaim(
                "\n########## Forms filling ##########\n");
        } catch (NoSuchClaim e) {
            return;
        }
        if (!claim.isResponseFormSet()) {
            System.out.println("No Response Form for this claim generated yet" +
                    ". Operation aborted.\n");
            return;
        }
        System.out.println("Here are the current form fields and their values :\n");
        StringBuilder str = new StringBuilder();
        Integer index = 0;
        Iterator iter = claim.getResponseForm().getFields().keySet().iterator();
        String key = "";
        String value = "";
        ArrayList<String> keys = new ArrayList();

        while(iter.hasNext()) {
            key = (String)iter.next();
            keys.add(key);
            value = claim.getResponseForm().getFields().get(key);
            str.append(index+1).append(". ");
            if ("".equals(value)) {
                str.append(key).append(" : <NO VALUE>\n");
            } else {
                str.append(key).append(" : ").append(value).append("\n");
            }
            index++;
        }

        System.out.println(str.toString());
        
        /* Actual field filling */
        boolean leave = false;
        String result1 = "";
        Integer resultInt = (-1);
        boolean keepModifying = false;
        while(!leave) {
            if (claim.getResponseForm().isFormComplete() && !keepModifying) {
                result1 = Saisie.lireChaine("Form completed. Do you want to " +
                        "validate it ? 'y' for validation, anything else to " +
                        "keep modifying it.\nYour choice : ");
                if (result1.equals("y")) {
                    comp.formFillAndUnClassifyClaim(claim);
                    return;
                } else {
                    keepModifying = true;
                }
            }
            result1 = Saisie.lireChaine("Enter a field number or press ENTER "+
                "if you don't want to set any.\nYour choice : ");
            if (result1.equals("")) {
                break;
            }
            keepModifying = false;
            try{
                resultInt = Integer.parseInt(result1)-1;
            } catch (NumberFormatException e) {
                System.out.println("A number was required, filling aborted...");
                System.out.println("\n###################################\n");
                return;
            }
            boolean test = false;
            try {
                test = claim.getResponseForm().getFields().keySet().
                    contains(keys.get(resultInt));
            } catch (IndexOutOfBoundsException e) {
                System.out.println("Invalid value");
            }
            if (test) {
                claim.getResponseForm().getFields().put(keys.get(resultInt),
                        Saisie.lireChaine("Your new value : "));
            } else {
                leave = true;
            }
        }
        System.out.println("\n###################################\n");
    }
    
    private void processClaim() {
        Claim claim = null;
        try {
            claim = askForFormFilledClaim(
                "########## Claim processing ##########\n");
        } catch (NoSuchClaim e) {
            return;
        }
        /* If the claim is only classified, we check the insurance */
        if (claim.getStatus()==Claim.Status.CLASSIFIED) {
            checkInsurance(claim);
        }
        /* If the claim is complex and it's insurance was checked */
        if (claim instanceof ComplexClaim &&
                claim.getStatus()==Claim.Status.INSURANCECHECKED) {
            checkDamageHistory(claim);
        }
        /* If the claim is complex and the damage history was checked */
        if (claim instanceof ComplexClaim &&
                claim.getStatus()==Claim.Status.DAMAGEHISTORYCHECKED) {
            phoneGarage(claim);
        }
        /* If the claim is simple and it's insurance was checked */
        if (claim instanceof SimpleClaim &&
                claim.getStatus()==Claim.Status.INSURANCECHECKED) {
            phoneGarage(claim);
        }
        System.out.println("\n###################################\n");
    }

    private void checkInsurance(Claim claim) {
        System.out.println("\n####### Insurance checking #######\n");
        try {
            Insurance ins = claim.getCustomer().checkInsurance(
                    claim.getResponseForm().getFields().get("Car registration"));
            /*String date = ins.getEndOfValidity().getDisplayName(
                     Calendar.DAY_OF_WEEK, Calendar.LONG, Locale.ENGLISH)
                    + " " + ins.getEndOfValidity().getDisplayName(
                    Calendar.DAY_OF_MONTH, Calendar.LONG, Locale.ENGLISH) + " "
                    + ins.getEndOfValidity().getDisplayName(
                    Calendar.MONTH, Calendar.LONG, Locale.ENGLISH) + " "
                    + ins.getEndOfValidity().getDisplayName(
                    Calendar.YEAR, Calendar.LONG, Locale.ENGLISH);*/
            if ( ins.getEndOfValidity().after(claim.getAccidentDate() ) ) {
                System.out.println("The car of the customer is insured with "
                        + "the insurance #" + ins.getId() + " which is valid "
                        + "until " + ins.getEndOfValidity().getTime() + ".");
            }
            else {
                System.out.println("The car of the customer is no longer "
                        + "insured with the insurance #" + ins.getId()
                        + "since " + ins.getEndOfValidity().getTime() + ".");
            }
        } catch (NoInsuranceForThisCar ex) {
            System.out.println(ex);
        }
        finally {
            System.out.println("\n###################################\n");
        }
        validateStep(claim, Claim.Status.INSURANCECHECKED);
    }

    private void checkDamageHistory(Claim claim) {
        System.out.println("\n####### Damage History Checking #######\n");
        try {
            System.out.println(claim.getCustomer().checkDamageHistoryText(claim));
        } catch (NoDamageHistory ex) {
            System.out.println("There is no damage history in the year "+
                    "before this claim.\n");
        }
        System.out.println("\n###################################\n");
        validateStep(claim, Claim.Status.DAMAGEHISTORYCHECKED);
    }

    private void phoneGarage(Claim claim) {
        System.out.println("\n####### garage phoning #######\n");
        String garagePhoneNumber = "";
        String garageRef = claim.getResponseForm().getFields().
                get("Garage reference");
        for ( Garage g : comp.getAffiliatedGarages() ) {
            if ( g.getReference().equalsIgnoreCase(garageRef) ) {
                garagePhoneNumber = g.getPhoneNumber();
                break;
            }
        }
        if ( !garagePhoneNumber.equals("") ) {
            System.out.println("Please call the garage at this number : "
                    + garagePhoneNumber);
            validateStep(claim, Claim.Status.GARAGEPHONED);
            if ( claim.getStatus().equals(Claim.Status.GARAGEPHONED) ) {
                comp.validateAndUnFormFilledClaim(claim);
            }
        }
        else {
            System.out.println("The garage reference given does not match an "
                    + "affiliated garage. Please ask once more to the "
                    + "customer to give the garage reference.");
        }
        System.out.println("\n###################################\n");
    }
    
    private void validateStep(Claim claim, Claim.Status status) {
                String answer = Saisie.lireChaine("Press 1 to validate this "
                        + "step or any other key to cancel this operation.\n"
                        + "Your choice : ");
        if ("1".equals(answer)) {
            claim.setStatus(status);
        }
    }

    private void takeDecision() {
        Claim claim = null;
        try {
            claim = askForValidatedClaim(
                "########## Taking a decision about a claim ##########\n");
        } catch (NoSuchClaim e) {
            return;
        }
        System.out.println("\nWhat would you like to do about " +
                "this claim :\n" +
                "\t0. Quit\n" +
                "\t1. Take the claim in charge\n" +
                "\t2. Reject the claim\n");
        boolean valid = false;
        while(!valid) {
            String result = Saisie.lireChaine("Your choice : ");
            switch (result) {
                case "1":
                    valid = true;
                    /* Request paiement */
                    Garage receiver = null;
                    String ref = claim.getResponseForm().getFields().get(
                            "Garage reference");
                    for ( Garage g : comp.getAffiliatedGarages() ) {
                        if ( g.getReference().equalsIgnoreCase(ref)) {
                            receiver = g;
                            break;
                        }
                    }
                    /* Garage found thanks to its reference */
                    if ( receiver != null ) {
                        /* Calculate amount to reimburse */
                        claim.treat();
                        /* Send paiement request to Finance department */
                        Paiement request = new Paiement(receiver, claim);
                        comp.requestPaiement(request);
                        System.out.println("A paiement request has been sent "
                                + "to the Finance department");
                    }
                    else {
                        System.out.println("The reference given in the form "
                                + "does not match an affiliated garage, "
                                + "it's not possible to take a decision...");
                        return;
                    }
                    generateResponseLetter(claim, true);
                    comp.validateForPaymentAndUnValidateClaim(claim);
                    claim.setStatus(Claim.Status.VERIFIED);
                    break;
                case "2":
                    valid = true;
                    generateResponseLetter(claim, false);
                    comp.validateForPaymentAndUnValidateClaim(claim);
                    claim.setStatus(Claim.Status.VERIFIED);
                    break;
                case "0":
                    return;
                default:
                    System.out.println("Invalid answer. Try again.");
                    break;
            }
        }
    }
    
    private void generateResponseLetter(Claim claim, boolean accepted) {
        System.out.println("\n########## Generate response letter ##########\n");
        System.out.println("What type of response would you like to generate :\n" +
                "\t1. Regular mail\n" +
                "\t2. Email\n" + 
                "\t3. SMS\n");
        boolean valid = false;
        while (!valid) {
            String result = Saisie.lireChaine("Your choice : ");
            switch (result) {
                case "1":
                    valid = true;
                    System.out.println("Letter generated : \n##########");
                    System.out.println(new ResponseLetter(comp, claim, emp,
                            accepted, "letter").getResult());
                    break;
                case "2":
                    valid = true;
                    System.out.println("Email generated : \n##########");
                    System.out.println(new ResponseLetter(comp, claim, emp,
                            accepted, "email").getResult());
                    break;
                case "3":
                    valid = true;
                    System.out.println("SMS generated : \n##########");
                    System.out.println(new ResponseLetter(comp, claim, emp,
                            accepted, "sms").getResult());
                    break;
                default:
                    valid = true;
                    System.out.println("Letter generated : \n##########");
                    System.out.println(new ResponseLetter(comp, claim, emp,
                            accepted, "letter").getResult());
            }
        }
    }
    
    private Claim askForClassifiedClaim(String introduction) throws NoSuchClaim {
        System.out.println(introduction);
        if (comp.getClassifiedClaims().isEmpty()) {
            System.out.println("No classified claims at this time.\n");
            throw new NoSuchClaim();
        }
        Claim claim = null;
        Integer tries = 0;
        System.out.println("List of classified claims :\n");
        for (Claim c : comp.getClassifiedClaims()) {
            System.out.println("\t" + c.getIdClaim() + ". Claim from customer '"
                    + c.getCustomer().getFirstName() + " "
                    + c.getCustomer().getLastName() + "'.");
        }
        System.out.println("\nPlease select the claim by providing its number "
                + "as given in the previous list.");
        while (claim == null && tries < 3) {
            try {
                Integer claimId = Integer.parseInt(Saisie.lireChaine("Your choice : "));
                for (Claim c : comp.getClassifiedClaims()) {
                    if (c.getIdClaim() == claimId) {
                        claim = c;
                    }
                }
            } catch (NumberFormatException e) {
                System.out.println("A number is required.");
            }
            tries++;
        }
        if (claim == null) {
            System.out.println("Too many errors in claim selection, "
                    + "operation aborted...");
            throw new NoSuchClaim();
        }
        return claim;
    }

    private Claim askForFormFilledClaim(String introduction) throws NoSuchClaim {
        System.out.println(introduction);
        if (comp.getFormFilledClaims().isEmpty()) {
            System.out.println("No claims with form filled at this time.\n");
            throw new NoSuchClaim();
        }
        Claim claim = null;
        Integer tries = 0;
        System.out.println("List of claims with their form already filled :\n");
        for (Claim c : comp.getFormFilledClaims()) {
            System.out.println("\t" + c.getIdClaim() + ". Claim from customer '"
                    + c.getCustomer().getFirstName() + " "
                    + c.getCustomer().getLastName() + "'.");
        }
        System.out.println("\nPlease select the claim by providing its number "
                + "as given in the previous list.");
        while (claim == null && tries < 3) {
            try {
                Integer claimId = Integer.parseInt(Saisie.lireChaine("Your choice : "));
                for (Claim c : comp.getFormFilledClaims()) {
                    if (c.getIdClaim() == claimId) {
                        claim = c;
                    }
                }
            } catch (NumberFormatException e) {
                System.out.println("A number is required.");
            }
            tries++;
        }
        if (claim == null) {
            System.out.println("Too many errors in claim selection, "
                    + "operation aborted...");
            throw new NoSuchClaim();
        }
        return claim;
    }
    
    private Claim askForValidatedClaim(String introduction) throws NoSuchClaim {
        System.out.println(introduction);
        if (comp.getValidatedClaims().isEmpty()) {
            System.out.println("No validated claims at this time.\n");
            throw new NoSuchClaim();
        }
        Claim claim = null;
        Integer tries = 0;
        System.out.println("List of claims already validated :\n");
        for (Claim c : comp.getValidatedClaims()) {
            System.out.println("\t" + c.getIdClaim() + ". Claim from customer '"
                    + c.getCustomer().getFirstName() + " "
                    + c.getCustomer().getLastName() + "'.");
        }
        System.out.println("\nPlease select the claim by providing its number "
                + "as given in the previous list.");
        while (claim == null && tries < 3) {
            try {
                Integer claimId = Integer.parseInt(Saisie.lireChaine("Your choice : "));
                for (Claim c : comp.getValidatedClaims()) {
                    if (c.getIdClaim() == claimId) {
                        claim = c;
                    }
                }
            } catch (NumberFormatException e) {
                System.out.println("A number is required.");
            }
            tries++;
        }
        if (claim == null) {
            System.out.println("Too many errors in claim selection, "
                    + "operation aborted...");
            throw new NoSuchClaim();
        }
        return claim;
    }

    private void processPaiement() {
        System.out.println("\n####### Paiement processing #######\n");
        if ( comp.getPaiementsRequested().isEmpty() ) {
            System.out.println("No paiements requested by CD Department "
                    + "at this time.");
        }
        else {
            /* Paiement selection */
            System.out.println("Which one of the following paiements do you"
                    + " want to process ?\n");
            
            for (Paiement p : comp.getPaiementsRequested() ) {
                System.out.println("\t" + p.getIdPaiement() + ". Paiement of "
                        + p.getClaim().getExpensesTotalToReimburse() + "€ for "
                        + "garage '"+ p.getGarageReceiver().getName() + "'");
            }
            Paiement paiementSelected = null;
            Integer tries = 0;
            System.out.println("\nPlease select the paiement by providing its number "
                    + "as given in the previous list.");
            while (paiementSelected == null && tries < 3) {
                try {
                    Integer paiementId = Integer.parseInt(Saisie.lireChaine("Your choice : "));
                    for (Paiement p : comp.getPaiementsRequested() ) {
                        if (p.getIdPaiement() == paiementId) {
                            paiementSelected = p;
                            break;
                        }
                    }
                } catch (NumberFormatException e) {
                    System.out.println("A number is required.");
                }
                tries++;
            }
            /* Too many input errors, return to main menu */
            if (paiementSelected == null) {
                System.out.println("Too many errors in claim selection, "
                        + "paiement processing aborted...");
                return;
            }
            /* Process the paiement */
            else {
                Garage receiver = paiementSelected.getGarageReceiver();
                FinantialTransactionStub transaction = new
                        FinantialTransactionStub();
                /* Process financial transaction */
                transaction.processTransaction(receiver.getIban(),
                        paiementSelected.getClaim().getExpensesTotalToReimburse());
                /* Update garage paiements */
                receiver.addPaiement(paiementSelected);
                /* Update paiements list of the company */
                comp.processPaiement(paiementSelected);
            }
        }
        System.out.println("\n###################################\n");
    }

    private void printAllClaims() {
        System.out.println("\n######### All claims ###########\n");
        boolean claimsPresent = false;
        if (!comp.getRegisteredClaims().isEmpty()) {
            System.out.println("#### Registered claims ####");
        }
        for (Claim c : comp.getRegisteredClaims()) {
            System.out.println("Claim : "+c.getIdClaim() + "\t Status : "
                    + c.getStatus());
            claimsPresent = true;
        }
        if (!comp.getClassifiedClaims().isEmpty()) {
            System.out.println("#### Classified claims ####");
        }
        for (Claim c : comp.getClassifiedClaims()) {
            System.out.println("Claim : "+c.getIdClaim() + "\t Status : "
                    + c.getStatus());
            claimsPresent = true;
        }
        if (!comp.getFormFilledClaims().isEmpty()) {
            System.out.println("#### Form-filled claims ####");
        }
        for (Claim c : comp.getFormFilledClaims()) {
            System.out.println("Claim : "+c.getIdClaim() + "\t Status : "
                    + c.getStatus());
            claimsPresent = true;
        }
        if (!comp.getValidatedClaims().isEmpty()) {
            System.out.println("#### Pre-validated claims ####");
        }
        for (Claim c : comp.getValidatedClaims()) {
            System.out.println("Claim : "+c.getIdClaim() + "\t Status : "
                    + c.getStatus());
            claimsPresent = true;
        }
        if (!comp.getValidatedForPaymentClaims().isEmpty()) {
            System.out.println("#### Validated for payment claims ####");
        }
        for (Claim c : comp.getValidatedForPaymentClaims()) {
            System.out.println("Claim : "+c.getIdClaim() + "\t Status : "
                    + c.getStatus());
            claimsPresent = true;
        }
        if (!claimsPresent) {
            System.out.println("Currently no claims in the system.");
        }
        System.out.println("\n###########################\n");
    }

    private void consultGarageInfo() {
        System.out.println("\n####### Garage information consulting #######\n");
        if ( comp.getAffiliatedGarages().isEmpty() ) {
            System.out.println("No affiliated garages at this time.");
        }
        else {
            String ref = Saisie.lireChaine("Garage reference : ");
            Boolean found = false;
            for ( Garage g : comp.getAffiliatedGarages() ) {
                if ( g.getReference().equalsIgnoreCase(ref)) {
                    found = true;
                    /* Print garage information */
                    System.out.println("Information about the garage requested :");
                    System.out.println(g);
                    break;
                }
            }
            if ( !found ) {
                System.out.println("No garage found with this reference.");
            }
        }
        System.out.println("\n###################################\n");
    }
}
